/*
    curlplusplus - a c++ wrapper around libcurl
    Copyright (C) 2012  Francesco "Franc[e]sco" Noferi (francesco1149@gmail.com)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the
    Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
    Boston, MA  02110-1301, USA.
*/

#include "../include/curlplusplus/easy.hpp" // needs full declaration of easy, which also includes multi.hpp

namespace curlpp
{
    // multi methods
    multi::multi() throw(const exception &)
    {
        curl = curl_multi_init();

        if (!curl)
            throw exception("multi: failed to initialize curl");
    }

    multi::~multi() throw(const exception &)
    {
        attempt(curl_multi_cleanup(curl), "~multi: failed to cleanup curl - ");
    }

    void multi::add_handle(easy *easy_handle) throw(const exception &)
    {
        attempt(curl_multi_add_handle(curl, easy_handle->curl), "multi.add_handle: failed to add easy handle - ");
        handles.push_back(easy_handle);
    }

    easy *multi::get_handle(CURLMsg *msg) throw()
    {
        for (std::list<easy *>::iterator it = handles.begin(); it != handles.end(); it++)
        {
            easy *ptr = *it;

            if (ptr->curl == msg->easy_handle)
                return ptr;
        }

        return NULL;
    }

    void multi::remove_handle(easy *easy_handle) throw(const exception &)
    {
        attempt(curl_multi_remove_handle(curl, easy_handle->curl), "multi.add_handle: failed to add easy handle - ");
        handles.remove(easy_handle);
    }

    void multi::fdset(fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd)
        throw(const exception &)
    {
        attempt(curl_multi_fdset(curl, read_fd_set, write_fd_set, exc_fd_set, max_fd),
                "multi.fdset: failed to extract file descriptor information");
    }

    CURLMsg *multi::info_read(int *msgs_in_queue) throw()
    {
        return curl_multi_info_read(curl, msgs_in_queue);
    }

    void multi::socket_action(curl_socket_t sockfd, int ev_bitmask, int *running_handles) throw(const exception &)
    {
        attempt(curl_multi_socket_action(curl, sockfd, ev_bitmask, running_handles), "multi.socket_action: error - ");
    }

    bool multi::perform(int *running_handles) throw(const exception &)
    {
        CURLMcode res;

        res = curl_multi_perform(curl, running_handles);

        if (res == CURLM_CALL_MULTI_PERFORM)
            return true;

        attempt(res, "multi.perform: failed to perform multi cURL - ");

        return false;
    }

    void multi::timeout(long *timeout)
    {
        attempt(curl_multi_timeout(curl, timeout), "multi.timeout: could not obtain timeout - ");
    }

    void multi::attempt(CURLMcode res, std::string errortext) throw(const exception &)
    {
        if (res != CURLM_OK)
            throw exception(errortext + curl_multi_strerror(res));
    }
}
